package wodegua.d29_多线程;

/**
 * 同步代码块
 * 作用:把访问共享资源的核心代码给上锁，以此保证线程安全。
 * synchronized(同步锁){访问共享资源的核心代码}
 * 原理:每次只允许一个线程加锁后进入，执行完毕后自动解锁，其他线程才可以进来执行。
 *
 * 同步锁的注意事项
 * 对于当前同时执行的线程来说，同步锁必须是同一把(同一个对象)，否则会出bug。
 *
 * 锁对象随便选择一个唯一的对象好不好呢?
 * 不好，会影响其他无关线程的执行。
 *
 * 锁对象的使用规范
 * 1.建议使用共享资源作为锁对象，对于实例方法建议使用this作为锁对象。
 * 2.对于静态方法建议使用字节码(类名.class)对象作为锁对象。
 */
public class d3_thread_safe_sync_code {
    public static void main(String[] args) {
        AccountSyncCode account = new AccountSyncCode("id-1101",100000);
        new DrawThreadSyncCode(account, "小明").start();
        new DrawThreadSyncCode(account, "小红").start();

        AccountSyncCode account2 = new AccountSyncCode("id-1102",100000);
        new DrawThreadSyncCode(account2, "小黑").start();
        new DrawThreadSyncCode(account2, "小白").start();
    }
}

class AccountSyncCode{
    private String cardId;
    private double money;

    public AccountSyncCode() {
    }

    public AccountSyncCode(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public void DrawMoney(double money){
        String name = Thread.currentThread().getName();
        synchronized (this) {
            if (this.money >= money) {
                System.out.println(name + "来取" + money);
                this.money -= money;
                System.out.println(name + "来取" + money + "后，余额="+this.money);
            } else {
                System.out.println(name + "来取钱余额不足");
            }
        }
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}

class DrawThreadSyncCode extends Thread{
    private AccountSyncCode account;
    public DrawThreadSyncCode() {
    }

    public DrawThreadSyncCode(AccountSyncCode account, String name) {
        super(name);
        this.account = account;
    }

    @Override
    public void run() {
        account.DrawMoney(100000);
    }
}